package com.gitee.mocoffee.hclient;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

import com.google.common.base.Strings;
import com.google.common.net.HostAndPort;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.ToString;
import lombok.experimental.FieldDefaults;

/**
 * @author lijia
 */
@FieldDefaults(level = AccessLevel.PRIVATE)
@Getter
@ToString
public class Hrequest {
    public enum Hmethod {GET, POST, PUT, HEAD, DELETE, PATCH, OPTIONS, TRACE}

    public enum ProxyType {NONE, HTTP, SOCKS}

    @Getter
    @FieldDefaults(level = AccessLevel.PRIVATE)
    public static class HrequestBuilder {
        String              url;
        Hmethod             method         = Hmethod.GET;
        Map<String, String> cookies;
        Map<String, String> headers;
        String              mime           = "text/plain";
        String              reqCharset     = StandardCharsets.UTF_8.name();
        String              resCharset     = StandardCharsets.UTF_8.name();
        Map<String, String> params;
        String              postString;
        byte[]              postBody;
        int                 connTimeout    = 5000; // 默认连接超时5秒
        int                 readTimeout    = 30000; // 默认读取超时30秒
        boolean             followRedirect = true; //默认自动重定向
        int                 retryMax       = 0; //默认不重试
        long                retryInterval  = 1000; //默认重试间隔1秒
        ProxyType           proxyType      = ProxyType.NONE;//默认不使用代理
        HostAndPort         proxy;//代理服务器
        String[]            proxyCredential;//代理用户名密码
        boolean             proxyDNS;//是否由代理服务器解析dns

        private HrequestBuilder(String url, Hmethod method) {
            this.url = url;
            this.method = method;
            this.cookies = new HashMap<>(0);
            this.headers = new HashMap<>(0);
            this.params = new HashMap<>(0);
        }

        private HrequestBuilder(String url) {
            this(url, Hmethod.GET);
        }

        /**
         * 设置重试次数和间隔
         *
         * @param retryMax
         * @param retryInterval
         * @return
         */
        public HrequestBuilder withRetry(int retryMax, long retryInterval) {
            this.retryMax = retryMax;
            this.retryInterval = retryInterval;
            return this;
        }

        /**
         * 设置是否自动重定向
         *
         * @param followRedirect
         * @return
         */
        public HrequestBuilder withFollowRedirect(boolean followRedirect) {
            this.followRedirect = followRedirect;
            return this;
        }

        /**
         * 添加cookie
         *
         * @param name
         * @param value
         * @return
         */
        public HrequestBuilder addCookie(String name, String value) {
            this.cookies.put(name, value);
            return this;
        }

        /**
         * 添加header
         *
         * @param name
         * @param value
         * @return
         */
        public HrequestBuilder addHeader(String name, String value) {
            this.headers.put(name, value);
            return this;
        }

        /**
         * 添加请求参数
         *
         * @param name
         * @param value
         * @return
         */
        public HrequestBuilder addParam(String name, String value) {
            this.params.put(name, value);
            return this;
        }

        /**
         * 设置代理服务器
         *
         * @param proxyType
         * @param proxyHost
         * @param proxyPort
         * @param proxyUser
         * @param proxyPwd
         * @return
         */
        public HrequestBuilder withProxy(ProxyType proxyType, String proxyHost, int proxyPort, String proxyUser,
                                         String proxyPwd) {
            this.proxyType = proxyType;
            this.proxy = HostAndPort.fromParts(proxyHost, proxyPort);
            if (!Strings.isNullOrEmpty(proxyUser) && !Strings.isNullOrEmpty(proxyPwd))
                this.proxyCredential = new String[]{proxyUser, proxyPwd};
            return this;
        }

        /**
         * 设置是否让代理服务器解析DNS
         *
         * @param proxyDNS
         * @return
         */
        public HrequestBuilder withProxyDNS(boolean proxyDNS) {
            this.proxyDNS = proxyDNS;
            return this;
        }

        /**
         * 设置请求的mime
         *
         * @param mime
         * @return
         */
        public HrequestBuilder withMime(String mime) {
            this.mime = mime;
            return this;
        }

        /**
         * 设置请求的字符编码
         *
         * @param charset
         * @return
         */
        public HrequestBuilder withRequstCharset(String charset) {
            this.reqCharset = charset;
            return this;
        }

        /**
         * 设置响应的字符编码
         *
         * @param charset
         * @return
         */
        public HrequestBuilder withResponseCharset(String charset) {
            this.resCharset = charset;
            return this;
        }

        /**
         * 设置POST的字符串
         *
         * @param string
         * @return
         */
        public HrequestBuilder withPostString(String string) {
            this.postString = string;
            return this;
        }

        /**
         * 设置POST的请求体
         *
         * @param body
         * @return
         */
        public HrequestBuilder withPostBody(byte[] body) {
            this.postBody = body;
            return this;
        }

        /**
         * 设置连接超时
         *
         * @param connTimeout
         * @return
         */
        public HrequestBuilder withConnTimeout(int connTimeout) {
            this.connTimeout = connTimeout;
            return this;
        }

        /**
         * 设置读取超时
         *
         * @param readTimeout
         * @return
         */
        public HrequestBuilder withReadTimeout(int readTimeout) {
            this.readTimeout = readTimeout;
            return this;
        }

        public Hrequest build() {
            Hrequest request = new Hrequest();
            request.url = this.url;
            request.method = this.method;
            request.cookies = this.cookies;
            request.headers = this.headers;
            request.params = this.params;
            request.mime = this.mime;
            request.reqCharset = this.reqCharset;
            request.resCharset = this.resCharset;
            request.postString = this.postString;
            request.postBody = this.postBody;
            request.connTimeout = this.connTimeout;
            request.readTimeout = this.readTimeout;
            request.proxyType = this.proxyType;
            request.proxy = this.proxy;
            request.proxyCredential = this.proxyCredential;
            request.proxyDNS = this.proxyDNS;
            request.retryMax = this.retryMax;
            return request;
        }
    }

    String              url;
    Hmethod             method;        // 请求方式
    Map<String, String> cookies;       // 发送的cookie列表
    Map<String, String> headers;       // 发送的请求头
    String              mime;
    String              reqCharset;    // 请求的字符编码
    String              resCharset;    // 预计响应的字符编码
    Map<String, String> params;        // 请求参数,如果是get请求会拼接到url里;如果是post请求会作为表单
    String              postString;    // post文本
    byte[]              postBody;      // post二进制块数据
    int                 connTimeout;   // 连接超时
    int                 readTimeout;   // 读取超时
    boolean             followRedirect;// 是否自动处理30X跳转
    int                 retryMax;      // 最大重试次数,0表示不重试
    long                retryInterval; // 重试时间间隔,毫秒为单位
    ProxyType           proxyType;//代理类型
    HostAndPort         proxy;//代理
    String[]            proxyCredential;//代理用户名密码
    boolean             proxyDNS;//是否由代理服务器解析dns

    private Hrequest() {}

    public static HrequestBuilder builder(String url, Hmethod method) {
        return new HrequestBuilder(url, method);
    }

    public static HrequestBuilder builder(String url) {
        return new HrequestBuilder(url);
    }

    public boolean needProxy() {
        return proxyType != null && proxyType != ProxyType.NONE && proxy != null;
    }

    public boolean needProxyAuth() {
        return proxyCredential != null && proxyCredential.length == 2;
    }
}
